package com.bw.user.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.bw.common.utils.R;
import com.bw.user.feign.FeignGoodsServer;
import com.bw.common.utils.IdWorker;
import org.apache.commons.lang3.BooleanUtils;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bw.common.utils.PageUtils;
import com.bw.common.utils.Query;

import com.bw.user.dao.OrderInfoDao;
import com.bw.user.entity.OrderInfoEntity;
import com.bw.user.service.OrderInfoService;


@Service("orderInfoService")
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoDao, OrderInfoEntity> implements OrderInfoService {
    @Autowired
    private FeignGoodsServer feignGoodsServer;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderInfoEntity> page = this.page(
                new Query<OrderInfoEntity>().getPage(params),
                new QueryWrapper<OrderInfoEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public R addOrder(OrderInfoEntity orderInfo) {
        // 1、redis查询看该用户有没有下单
        Object goodsSeckillOrder = redisTemplate.opsForHash().get("goodsSeckillOrder", orderInfo.getUserId().toString() + orderInfo.getGoodsId());
        if (goodsSeckillOrder != null) {
            // 说明用户已经购买过这个商品
            return R.error("您已购买过该商品了，请勿查询下单。");
        } else {
            // 用户没有购买过商品 或者 没有购买过该商品
            Object goodsSeckillStockFlag = redisTemplate.opsForHash().get("goodsSeckillStockFlag", orderInfo.getGoodsId().toString());
            if (goodsSeckillStockFlag == null || goodsSeckillStockFlag.toString().equals("1")) {
                // 占坑
                String uuid = UUID.randomUUID().toString().replace("-", "");
                Boolean lock = redisTemplate.opsForValue().setIfAbsent("seckillOrderLock", uuid, 30, TimeUnit.SECONDS);
                if (BooleanUtils.isTrue(lock)) {
                    try {
                        // 有库存 库存-1
                        Integer flag = feignGoodsServer.setSeckillStock(orderInfo.getGoodsId(), -1);
                        if (flag > 0) {
                            // 订单使用消息队列异步下单， 订单消息队列异步生成，为什么使用消息队列 (0.5天)
                            orderInfo.setId(new IdWorker().nextId());
                            orderInfo.setCreateDate(new Date());
                            this.save(orderInfo);
                            // 有库存 添加订单
                            redisTemplate.opsForHash().put("goodsSeckillOrder",orderInfo.getUserId().toString() + orderInfo.getGoodsId(), JSON.toJSONString(orderInfo));
                            // 订单放入mq 异步添加
                            // rabbitTemplate.convertAndSend("OrderAddQueue",JSON.toJSONString(orderInfo));
                            // 将订单的库存状态存入redis
                            redisTemplate.boundHashOps("goodsSeckillStockFlag").put(orderInfo.getGoodsId().toString(),"1");
                            redisTemplate.expire("goodsSeckillStockFlag",2,TimeUnit.HOURS);
                            return R.ok();
                        } else {
                            /*// 将减少的库存加回来
                            feignGoodsServer.setSeckillStock(orderInfo.getGoodsId(), 1);*/
                            // 将订单的库存状态存入redis
                            redisTemplate.boundHashOps("goodsSeckillStockFlag").put(orderInfo.getGoodsId().toString(),"0");
                            redisTemplate.expire("goodsSeckillStockFlag",2,TimeUnit.HOURS);
                            return R.error("该商品已卖完，下次早点来吧。");
                        }
                    } finally {
                        // 删除锁
                        String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                                "then\n" +
                                "    return redis.call(\"del\",KEYS[1])\n" +
                                "else\n" +
                                "    return 0\n" +
                                "end";
                        redisTemplate.execute(new DefaultRedisScript<Long>(script,Long.class), Arrays.asList("seckillOrderLock"),uuid);
                    }
                } else {
                    System.out.println("占坑失败(创建秒杀订单)");
                    // 占坑失败 自旋等待100ms 调用方法本身
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    return addOrder(orderInfo);
                }
            } else {
                return R.error("该商品已卖完，下次早点来吧。");
            }
        }

    }

    @Scheduled(cron = "0/20 * * * * *")
    public void checkOrder() {
        System.out.println(new Date());
        List<Object> list = redisTemplate.boundHashOps("goodsSeckillOrder").values();
        assert list != null;
        List<Object> collect = list.stream().filter(i -> {
            OrderInfoEntity orderInfoEntity = JSON.parseObject(JSON.toJSONString(JSON.parse(i.toString())), new TypeReference<OrderInfoEntity>() {
            });
            long time = orderInfoEntity.getCreateDate().getTime();
            long systemTime = System.currentTimeMillis();
            System.out.println("超过20分钟不支付的订单删除：" + (systemTime - time) / 1000 / 60);
            System.out.println((systemTime - time) / 1000 / 60 >= 20 && orderInfoEntity.getStatus() == 0);
            return (systemTime - time) / 1000 / 60 >= 20 && orderInfoEntity.getStatus() == 0;
        }).collect(Collectors.toList());

        for (Object o : collect) {
            OrderInfoEntity orderInfoEntity = JSON.parseObject(JSON.toJSONString(JSON.parse(o.toString())), new TypeReference<OrderInfoEntity>() {
            });
            // 删除数据库
            this.removeById(orderInfoEntity);
            // 删除redis
            redisTemplate.opsForHash().delete("goodsSeckillOrder", orderInfoEntity.getUserId().toString() + orderInfoEntity.getGoodsId().toString());
        }
    }
}